import json
from datetime import datetime

from PyQt6.QtCore import QObject, pyqtSignal, QFile
from PyQt6.QtNetwork import QUdpSocket, QTcpServer, QTcpSocket, QHostAddress

from server import MongoServer, FileServer


class WebServer(QObject):

    notifyLog = pyqtSignal(str)

    def __init__(self):
        super(WebServer, self).__init__()
        # Udp
        self.udpsocket = QUdpSocket()
        self.uport = 23233
        self.uaddr = QHostAddress.SpecialAddress.Broadcast
        # Tcp
        self.tcpfilesocket = QTcpSocket()
        self.tcpserver = QTcpServer()
        self.tport = 5555
        self.taddr = QHostAddress.SpecialAddress.Broadcast
        # 用户连接
        self.addrs = {}

    def init(self):
        # init udp
        self.udpsocket.bind(self.uport, QUdpSocket.BindFlag.ShareAddress or QUdpSocket.BindFlag.ReuseAddressHint)
        self.udpsocket.readyRead.connect(self.recvUdpData)
        # init tcp
        self.tcpserver.newConnection.connect(self.recvTcpData)
        self.tcpserver.listen(QHostAddress.SpecialAddress.Any, self.tport)

    def recvUdpData(self):
        while self.udpsocket.hasPendingDatagrams():
            size = self.udpsocket.pendingDatagramSize()
            rawdata, addr, port = self.udpsocket.readDatagram(size)
            data = rawdata.decode('utf-8')
            data = json.loads(data)
            self.handleData(data, addr, port, rawdata)

    def handleData(self, data, addr, port, rawdata):
        dtype = data['Type']
        username = data['Username']
        if dtype == 'Online':
            # 上线
            MongoServer.setUserOnline(username, True)
            # 向客户端发送好友列表
            focuses = MongoServer.getUserFocuses(username)
            data = WebServer.createDatagram(dtype, username, body=focuses)
            data = bytes(json.dumps(data), 'utf-8')
            self.udpsocket.writeDatagram(data, addr, port)
            self.addrs[username] = (addr, port)
            self.notifyLog.emit('%s 上线了' % username)
            # 向客户端发送累计消息
            for message in MongoServer.getReceivedMessages(username):
                mid = message['_id']
                del message['_id']
                data = bytes(json.dumps(message), 'utf-8')
                self.udpsocket.writeDatagram(data, addr, port)
                MongoServer.delSavedMessages(mid)
        elif dtype == 'Offline':
            # 下线
            MongoServer.setUserOnline(username, False)
            del self.addrs[username]
            self.notifyLog.emit('%s 下线了' % username)
        elif dtype == 'Message':
            # 发送文本消息
            peer = data['Peername']
            if MongoServer.isUserOnline(peer) and peer in self.addrs:
                # 直接发送给指定用户
                addr, port = self.addrs[peer]
                self.udpsocket.writeDatagram(rawdata, addr, port)
            else:
                # 保存到数据库
                MongoServer.saveMessage(data)
        elif dtype == 'File':
            # TODO 发送文件
            pass
        elif dtype == 'Notif':
            # 通知服务器接收文件信息
            [name, size] = data['Body']
            file = QFile('files/%s/%s' % (username, name))
            file.open(QFile.OpenModeFlag.WriteOnly)
            self.tcpfilesocket.connectToHost(QHostAddress(addr.toString()[7:]), self.tport)
        elif dtype == 'ReqFile':
            # TODO 下载文件
            pass
        elif dtype == 'TelUp':
            # TODO 视频通话 接通
            pass
        elif dtype == 'TelOff':
            # TODO 视频通话 结束
            pass

    def recvTcpData(self):
        socket = self.tcpserver.nextPendingConnection()
        file = None  # TODO
        FileServer.recvFile(file, socket)
        socket.close()

    def close(self):
        self.tcpfilesocket.abort()
        self.tcpserver.close()
        self.udpsocket.close()

    @staticmethod
    def createDatagram(dtype, user, peer='', body: any = ''):
        return {
            "Type": dtype,
            "Username": user,
            "Peername": peer,
            "Body": body,
            "Datetime": str(datetime.now())
        }
